#include "launch/node_parser.h"
#include "logger.h"
#include "utils/utils.h"
#include "utils/common_utils.h"
#include "launch/env_parser.h"
#include "launch/param_parser.h"
#include "watch_dog/watch_dog.h"
#include <unistd.h>

NodeParser::NodeParser()
{

}
NodeParser::~NodeParser() = default;

std::optional<LaunchNode> NodeParser::parser(const tinyxml2::XMLElement *element)
{
    std::optional<NodeInfo> nodeInfo = getNodeInfo(element);
    if (!nodeInfo)
    {
        return std::nullopt;
    }
    //env
    std::vector<std::string> env_strings;
    for (char **env = environ; *env; ++env)
    {
        env_strings.emplace_back(*env);
    }
    nodeInfo.value().envs = env_strings;

    LaunchNode launchNode;
    ROS_INFO("node parser pass, pkg: %s, type: %s, name: %s, args: %s, respawn: %s, respawnDelay: %ds",
             nodeInfo->pkg.c_str(), nodeInfo->type.c_str(), nodeInfo->name.c_str(), nodeInfo->args.c_str(),
             nodeInfo->respawn ? "true" : "false", nodeInfo->respawnDelay);

    launchNode.addNode(nodeInfo.value());
    //再看下node内部是否有嵌套，嵌套有env/remap/rosparam/param，这里只支持env/param
    const tinyxml2::XMLElement *childElement = element->FirstChildElement();
    if (nullptr != childElement)
    {
        //确实有内部元素
        parserInner(childElement, nodeInfo->name);//这里name作为命名空间
    }
    if ("nodelet" != nodeInfo->pkg && "nodelet" != nodeInfo->type)
    {
        pWatchDog->createNode(launchNode);//直接创建进程，nodelet不再启动
    }
    
    return launchNode;
}
bool NodeParser::parserInner(const tinyxml2::XMLElement *childElement, const std::string &ns)
{
    //只支持env/param
    do
    {
        if (!parserInnerElement(childElement, ns))
        {
            break;
        }
        childElement = childElement->NextSiblingElement();//下一个
    }
    while (nullptr != childElement);

    return false;
}
bool NodeParser::parserInnerElement(const tinyxml2::XMLElement *childElement, const std::string &ns)
{
    //只支持env/param
    const char *name = childElement->Name();
    if (nullptr == name)
    {
        ROS_ERROR("node inner element get name fail, xml: %s", Utils::formatElement(childElement).c_str());
        return false;
    }
    if (strncmp(name, "env", strlen("env")) == 0)
    {
        EnvParser envParser;
        if (!envParser.parser(childElement))
        {
            return false;
        }
    }
    else if (strncmp(name, "param", strlen("param")) == 0)
    {
        ParamParser paramParser(ns);
        if (!paramParser.parser(childElement))
        {
            return false;
        }
    }
    else
    {
        ROS_WARN("unsupported node inner element, name: %s", name);
    }
    return true;
}
std::optional<NodeInfo> NodeParser::getNodeInfo(const tinyxml2::XMLElement *element)
{
    NodeInfo nodeInfo;
    const char *attribute;
    //pkg
    tinyxml2::XMLError xmlError = element->QueryAttribute("pkg", &attribute);
    if (tinyxml2::XMLError::XML_SUCCESS != xmlError)
    {
        ROS_ERROR("node get pkg attribute fail");
        return std::nullopt;
    }
    nodeInfo.pkg = attribute;
    //type
    xmlError = element->QueryAttribute("type", &attribute);
    if (tinyxml2::XMLError::XML_SUCCESS != xmlError)
    {
        ROS_ERROR("node get type attribute fail");
        return std::nullopt;
    }
    nodeInfo.type = attribute;
    //name
    xmlError = element->QueryAttribute("name", &attribute);
    if (tinyxml2::XMLError::XML_SUCCESS != xmlError)
    {
        ROS_ERROR("node get name attribute fail");
        return std::nullopt;
    }
    nodeInfo.name = attribute;
    //execPath
    std::vector<std::string> execPathList = CommonUtils::getExecPath(nodeInfo.pkg, nodeInfo.type);
    if (execPathList.empty())
    {
        ROS_ERROR("exec file is not exist, pkg: %s, file: %s", nodeInfo.pkg.c_str(), nodeInfo.type.c_str());
        return std::nullopt;
    }
    if (execPathList.size() > 1)
    {
        ROS_ERROR("multiple exec file exist");
        return std::nullopt;
    }
    nodeInfo.execPath = execPathList.at(0);
    //args
    xmlError = element->QueryAttribute("args", &attribute);
    if (tinyxml2::XMLError::XML_SUCCESS == xmlError)
    {
        nodeInfo.args = attribute;
    }
    //respawn
    bool boolAttribute = false;
    xmlError = element->QueryAttribute("respawn", &boolAttribute);
    if (tinyxml2::XMLError::XML_SUCCESS == xmlError)
    {
        nodeInfo.respawn = boolAttribute;
    }
    //respawn_delay
    int intAttribute = 0;
    xmlError = element->QueryAttribute("respawn_delay", &intAttribute);
    if (tinyxml2::XMLError::XML_SUCCESS == xmlError)
    {
        nodeInfo.respawnDelay = intAttribute;
    }
    //output
    xmlError = element->QueryAttribute("output", &attribute);
    if (tinyxml2::XMLError::XML_SUCCESS == xmlError)
    {
        nodeInfo.printScreen = (strncmp(attribute, "screen", strlen("screen")) == 0);
    }
    return nodeInfo;
}
